package com.siemens.industrial.app;

import com.siemens.industrial.model.admin.Device;
import com.siemens.industrial.model.partner.DeviceState;
import com.siemens.industrial.model.partner.PositionStatePeriod;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Yipin on 2016/11/30.
 */
public class DeviceStatusImplementation {
    private static final String databaseTag = "{database}";
    private JdbcTemplate jdbcTemplate;
    private static final String getDevicesQuery = "SELECT * FROM `siemens`.`device` where `delete_time` is null";
    private static final String getDeviceById = "select * from siemens.device where device_id =?";
    private static final String findAllStatesByDeviceIdAndTime = "SELECT * FROM `" + databaseTag + "`.device_state where `device_id`= ? and `status_type`<>'0' and  date(`time`)=date(?) order by `status_type`,`time`;";
    private static final String findMaxByDateGroupByStatusType = "SELECT device_id,status_type, max(`time`) as 'time',state,source_ip,write_time FROM `" + databaseTag + "`.device_state  WHERE DATE(`time`) = DATE(?) AND `status_type` = ? AND `device_id` = ?;";
    private static final String insertPositionStatePeriod = "INSERT INTO `" + databaseTag + "`.`position_period` (`device_id`, `start_time`, `end_time`, `state`, `status_type`) VALUES (?, ?, ?, ?, ?);";
    private static final String deleteByDateAndDeviceId = "DELETE FROM `" + databaseTag + "`.`position_period` WHERE `device_id`=? and date(`start_time`)=date(?);";
    private static final String insertReissue = "INSERT INTO `siemens`.`temp_reissue_data` (`device_id`, `tenant_id`, `date`) VALUES (?, ?, ?);";
    private static final String getCountByAllParam = "select count(1) from `siemens`.`temp_reissue_data` where `device_id`=? and `tenant_id`=? and `date`=date(?)";
    private static final String findAllReissue = "select * from `siemens`.`temp_reissue_data`";
    private static final String deleteReissue = "DELETE FROM `siemens`.`temp_reissue_data` WHERE `device_id`=? and`tenant_id`=? and `date`=?;";

    public void deviceStatusDispose() {
        jdbcTemplate = DBHelper.getJdbcTemplate();
        if (jdbcTemplate == null)
            return;

        List<Device> deviceList = jdbcTemplate.query(getDevicesQuery,
                com.siemens.industrial.model.admin.Device.getRowMapper());

        if (deviceList == null || deviceList.size() == 0)
            return;

        for (com.siemens.industrial.model.admin.Device device : deviceList) {
            Date date = new Date();
            long time = DeviceStatusImplementation.getZero(date, true) / 1000;
            System.out.println(time);
            String sql_state = "call " + "`" + device.getTenantId() + "`" + ".p_device_state_period('" + device.getDeviceId() + "',from_unixtime(" + time + "));";
            jdbcTemplate.execute(sql_state);
            dataFormatOtherTypePeriod(device.getDeviceId(), device.getTenantId(), date);
        }

        List<Map<String, Object>> list = jdbcTemplate.queryForList(findAllReissue);

        for (Map<String, Object> map : list) {
            if (!map.containsKey("device_id")) {
                continue;
            }
            if (!map.containsKey("tenant_id")) {
                continue;
            }
            if (!map.containsKey("date")) {
                continue;
            }

            SimpleDateFormat sim = new SimpleDateFormat("yyyy-MM-dd");
            try {
                Date date = sim.parse(map.get("date").toString());
                dataFormatOtherTypePeriod(map.get("device_id").toString(), map.get("tenant_id").toString(), date);
                delete(map.get("device_id").toString(), map.get("tenant_id").toString(), date);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    public void deviceStatusDispose(String info) {
        jdbcTemplate = DBHelper.getJdbcTemplate();
        if (jdbcTemplate == null)
            return;
        Map<String, Object> map = SendMail.buildJsonToMap(info);
        if ((map.get("time") == null || map.get("time").equals("")) || (map.get("deviceId") == null || map.get("deviceId").equals("")))
            return;
        Map<String, Object> device = getOneDeviceById(map.get("deviceId").toString());
        if (device == null)
            return;

        String tenantId = device.get("tenant_id").toString();
        String database = "`" + tenantId + "`";
        String deviceId = device.get("device_id").toString();
        int statusType = Integer.parseInt(String.valueOf(map.get("statusType")));
        Date time = new Date(Long.parseLong(map.get("time").toString()));
        long date = getZero(time, false) / 1000;
        if (statusType == 0) {
            String sql = "call " + database + ".p_device_state_period('" + deviceId + "',from_unixtime(" + date + "));";
            jdbcTemplate.execute(sql);
        } else {
            //// TODO: 2017/3/1 其他状态补发 写入数据库
            if (getCount(deviceId, tenantId, time) > 0)
                return;
            insert(deviceId, tenantId, time);
        }

        System.out.println("deviceStatusDispose Have Param" + info);
    }

    private Map<String, Object> getOneDeviceById(String deviceId) {
        return jdbcTemplate.queryForMap(getDeviceById, deviceId);
    }

    public static long getZero(Date date, boolean isBefore) {
        System.out.print(date);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        if (isBefore) {
            calendar.add(Calendar.DAY_OF_MONTH, -1);
        }
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTimeInMillis();
    }

    private void dataFormatOtherTypePeriod(String deviceId, String tenantId, Date date) {
        List<DeviceState> list = jdbcTemplate.query(findAllStatesByDeviceIdAndTime.replace(databaseTag, tenantId), new Object[]{deviceId, date}, DeviceState.getRowMapper());

        List<PositionStatePeriod> dataSource = new ArrayList<>();

        int size = list.size();
        int current_type = 0;
        int current_state = 0;
        long start = 0;

        for (int i = 0; i < size; i++) {
            if (i == 0) {
                current_type = list.get(i).getStatusType();
                current_state = list.get(i).getState();
                long current_time = list.get(i).getTime().getTime();
                start = initZeroStart(current_time, current_type, current_state, dataSource, deviceId, tenantId);
                continue;
            }

            if (current_type != list.get(i).getStatusType()) {
                long current_time = list.get(i).getTime().getTime();
                long zero = getZero(new Date(current_time), false) + 1000 * 24 * 3600;
                PositionStatePeriod positionStatePeriod =
                        initialDeviceStatePeriod(deviceId, new Date(start), new Date(zero), current_state, current_type);
                dataSource.add(positionStatePeriod);
                current_type = list.get(i).getStatusType();
                current_state = list.get(i).getState();
                start = initZeroStart(current_time, current_type, current_state, dataSource, deviceId, tenantId);
                continue;
            }

            if (current_state != list.get(i).getState() || i > size - 1) {
                PositionStatePeriod positionStatePeriod = initialDeviceStatePeriod(deviceId, new Date(start), list.get(i).getTime(), current_state, current_type);
                dataSource.add(positionStatePeriod);
                current_type = list.get(i).getStatusType();
                current_state = list.get(i).getState();
                start = list.get(i).getTime().getTime();
            }

            if (i == size - 1) {
                long current_time = list.get(i).getTime().getTime();
                long zero = getZero(new Date(current_time), false) + 1000 * 24 * 3600;
                PositionStatePeriod positionStatePeriod = initialDeviceStatePeriod(deviceId, new Date(start), new Date(zero), current_state, current_type);
                dataSource.add(positionStatePeriod);
            }
        }
        delete(tenantId, new Date(), deviceId);
        insertBatch(dataSource, tenantId);
    }

    private long initZeroStart(long current_time, int current_type, int current_state, List<PositionStatePeriod> dataSource, String deviceId, String tenantId) {
        long start = 0;
        long zero = getZero(new Date(current_time), false);
        if (current_time != zero) {
            DeviceState ys = getDeviceMaxTimeGroupBy(tenantId, new Date(zero - 24 * 3600 * 1000), current_type, deviceId);
            if (ys.getDeviceId() != null) {
                if (ys.getState() == current_state) {
                    start = zero;
                } else {
                    PositionStatePeriod positionStatePeriod =
                            initialDeviceStatePeriod(deviceId, new Date(zero), new Date(current_time), ys.getState(), current_type);
                    dataSource.add(positionStatePeriod);
                    start = current_time;
                }
            } else {
                start = zero;
            }
        }
        return start;
    }

    private PositionStatePeriod initialDeviceStatePeriod(String deviceId, Date start, Date end, int state, int type) {
        PositionStatePeriod PositionStatePeriod = new PositionStatePeriod();
        PositionStatePeriod.setDeviceId(deviceId);
        PositionStatePeriod.setStartTime(start);
        PositionStatePeriod.setEndTime(end);
        PositionStatePeriod.setState(state);
        PositionStatePeriod.setStatusType(type);
        return PositionStatePeriod;
    }

    private DeviceState getDeviceMaxTimeGroupBy(String tenantId, Date date, int statusType, String deviceId) {
        return jdbcTemplate.queryForObject(findMaxByDateGroupByStatusType.replace(databaseTag, tenantId), new Object[]{date, statusType, deviceId}, DeviceState.getRowMapper());/////// TODO: 2017/3/14 ??????????????????//
    }

    private void delete(String tenantId, Date startTime, String deviceId) {
        jdbcTemplate.update(deleteByDateAndDeviceId.replace(databaseTag, tenantId), deviceId, startTime);
    }

    private void insertBatch(List<PositionStatePeriod> list, String tenantId) {
        jdbcTemplate.batchUpdate(insertPositionStatePeriod.replace(databaseTag, tenantId), new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
                PositionStatePeriod period = list.get(i);
                preparedStatement.setString(1, period.getDeviceId());
                preparedStatement.setTimestamp(2, new java.sql.Timestamp(period.getStartTime().getTime()));
                preparedStatement.setTimestamp(3, new java.sql.Timestamp(period.getEndTime().getTime()));
                preparedStatement.setInt(4, period.getState());
                preparedStatement.setInt(5, period.getStatusType());
            }

            @Override
            public int getBatchSize() {
                return list.size();
            }
        });
    }

    private int getCount(String deviceId, String tenantId, Date date) {
        return jdbcTemplate.queryForObject(getCountByAllParam, new Object[]{deviceId, tenantId, date}, Integer.class);
    }

    private void insert(String deviceId, String tenantId, Date date) {
        jdbcTemplate.update(insertReissue, deviceId, tenantId, date);
    }
    private void delete(String deviceId, String tenantId, Date date) {
        jdbcTemplate.update(deleteReissue, deviceId, tenantId, date);
    }
}

